package com.msstudio.db;

import java.sql.*;
import java.util.*;
import java.util.logging.Logger;

import com.mysql.jdbc.exceptions.jdbc4.CommunicationsException;

public class DBConnectionPool {
	private static final Logger logger = Logger.getLogger(DBConnectionPool.class.getName());

	private String dbUserName;
	private String dbPassword;
	private String connectionURL;
	private int maxConnection;
	private Vector<Connection> freeConnections;
	private int checkedOut;
	private Timer timer;
	private long maxidletimeInSeconds;

	public DBConnectionPool(String userName, String password, String url,
			String encode, String connOption,
			int maxConn, long maxidletimeInSeconds) {
		this.dbUserName = userName;
		this.dbPassword = password;
		this.connectionURL = url + "?&characterEncoding=" + encode +"&"+connOption;
		freeConnections = new Vector<Connection>();
		this.maxConnection = maxConn;
		this.maxidletimeInSeconds = maxidletimeInSeconds;
		start();
	}
	
	private void start()
	{
		timer = new Timer();
		timer.schedule(innerTimerTask(), 60 * 1000, maxidletimeInSeconds * 1000);
	}

	public synchronized Connection getDBConnection() {
		Connection conn = null;
		if (freeConnections.size() > 0) {
			conn = (Connection) freeConnections.elementAt(0);
			freeConnections.removeElementAt(0);
			try {
				if (conn.isClosed()) {
					// conn = getDBConnection();
					conn = newDBConnection();
					
				}
			} catch (SQLException ex) {
				conn = getDBConnection();
			}
		} else if (maxConnection == 0 || checkedOut < maxConnection) {
			conn = newDBConnection();
		}
		if (conn != null) {
			checkedOut++;
		}
		return (conn);
	}// using FIFO method to get connection instance

	public synchronized Connection getDBConnection(long timeout) {
		long startTime = new java.util.Date().getTime();
		Connection conn;
		while ((conn = getDBConnection()) == null) {
			try {
				wait(timeout);
			} catch (InterruptedException ex) {
			}
			if (new java.util.Date().getTime() - startTime >= timeout) {
				return (null);
			}
		}
		return conn;
	}

	public Connection newDBConnection() {
		Connection conn = null;		
		try {
			if (dbUserName == null) {
				conn = DriverManager.getConnection(connectionURL);
			} else {
				conn = DriverManager.getConnection(connectionURL, dbUserName,
						dbPassword);
			}
		} catch (SQLException ex) {
			logger.warning("Invalid connection url:" + connectionURL);
		}
		return (conn);
	}

	public synchronized void freeDBConnection(Connection conn) {
		freeConnections.addElement(conn);
		checkedOut--;
		notifyAll();
	}

	public synchronized void release() {
		Enumeration<Connection> allConnections = freeConnections.elements();
		while (allConnections.hasMoreElements()) {
			try {
				Connection conn = (Connection) allConnections.nextElement();
				conn.close();
			} catch (SQLException ex) {
				ex.printStackTrace();
			}
		}
		freeConnections.removeAllElements();
	}
	
	public Vector<Connection> getFreeConnections() {
		return this.getFreeConnections();
	}
	
	private TimerTask innerTimerTask()
	{
		return new TimerTask()
		{
			public synchronized void run()
			{
				if (DBConnectionPool.this.freeConnections.size() > 0)
				{
					for (int i = 0; i < freeConnections.size(); i++)
					{
						Connection conn = null;
						try
						{
							conn = (Connection) freeConnections.elementAt(i);
							if (conn!= null) {
								if (conn.isClosed()){
									freeConnections.remove(i);
									logger.info("Drop a invalid connection from the pool");
								} else { 
									conn.setAutoCommit(true);
								}
							}
						} catch (CommunicationsException e) {
							if (conn != null) {
								try {
									conn.close();
								} catch (Exception ex) {}																							
								freeConnections.remove(i);
								logger.info("Drop a invalid connection from the pool:" + e.getMessage());
							}
						} catch (SQLException e)
						{
							logger.info("Failed to drop a invalid connection from the pool ");
						} 
						
					}
				}
			}
		};
	}

}
